#!/usr/bin/env python

import time, os, commands, select, pdb, signal
import cgi

def if_true(check, ret, altret = ""):

   return (altret, ret)[check == True]

def rerange(xlist, outmax):

   xmax = max(xlist)

   if xmax == 0:
      return xlist

   return [ outmax * (xvalue / float(xmax)) for xvalue in xlist ]

def html_quote(v):
    if v is None:
       return ''
    return cgi.escape(str(v), 1)

def ksort(d, func = None):
    keys = d.keys()
    keys.sort(func)
    return keys

def strings(txt, min_len = 2):

   pos = 0
   toret = ""

   while pos < len(txt):

      x = txt[pos]
      xord = ord(x)

      if ( 97 <= xord and xord <= 122 ) or ( 65 <= xord and xord <= 90 ) \
        or ( 48 <= xord and xord <= 57 ) \
        or xord in [32, 58, 44, 46]:
        toret = toret + x

      elif len(toret) > 0:

        if len(toret) >= min_len:
           return toret
        else:
           return ""

      pos += 1

   return toret

def bsearch(l, value):
   lo, hi = 0, len(l)-1
   while lo <= hi:
      mid = (lo + hi) / 2
      if l[mid] < value:
         lo = mid + 1
      elif value < l[mid]:
         hi = mid - 1
      else:
         return mid

def remove_duplicates_from_list(xlist):
   d = {}
   for x in xlist: d[x]=x
   return d.values()

def current_year():
   return time.localtime()[0]

def userfriendly_size(size_in_bytes):

   size_in_bytes = float(size_in_bytes)

   if 0 < size_in_bytes < 1024 * 1024 * 1024:
      return "%1.1f MiB" % ( size_in_bytes / 1024 / 1024 )
   elif size_in_bytes < 1024 * 1024 * 1024 * 1024:
      return "%1.1f GiB" % ( size_in_bytes / 1024 / 1024 / 1024 )
   elif size_in_bytes < 1024 * 1024 * 1024 * 1024 * 1024:
      return "%1.1f TiB" % ( size_in_bytes / 1024 / 1024 / 1024 / 1024 )

   return "%d bytes" % size_in_bytes

def date_to_timestamp(date):
   return time.strftime("%Y%m%d%H%M%S", date)

def dict_sort_by_value_r(d):
   """ Returns the keys of dictionary d sorted by their values """
   items=d.items()
   backitems=[ [v[1],v[0]] for v in items]
   backitems.sort()
   backitems.reverse()
   return [ backitems[i][1] for i in range(0,len(backitems))]

class Memoize:
   """Memoize(fn) - an instance which acts like fn but memoizes its arguments
      Will only work on functions with non-mutable arguments
   """
   def __init__(self, fn):
      self.fn = fn
      self.memo = {}

   def __call__(self, *args):
      if not self.memo.has_key(args):
         self.memo[args] = self.fn(*args)
      return self.memo[args]

   def forget(self):
      self.mem = {}

class SQL:
   def  __init__(self):
      self.dbcon = sqlite.connect(":memory:", check_same_thread=False)
      self.dbcur = self.dbcon.cursor()

      self.dbcon.execute("create table events(date, host, position, message, css_style)")

   def execute(self, query):
      return self.dbcon.execute(query)

def color_gradient(src, dst, percent):
   csrc = [ col for col in src ]
   cdst = [ col for col in dst ]
   toret = []

   for inc in range(0,3):
     toret.append(csrc[inc] + ((cdst[inc] - csrc[inc]) * percent / 100))

   return toret

def rgb_to_hex(rgb):
   return "%X%X%X" % (rgb[0], rgb[1], rgb[2])


class command_runner:
   """ Execute a command and gather stdin, stdout, and return status.
   """

   def __init__(self, command, timeout = 300, readline_callback = None):

      # Log if binary is not runnable or does not exist
      for path in os.environ["PATH"].split(":"):
         cmdfile = command.strip("(").split()[0]
         # handle both absolute or relative paths
         if ( ( not os.path.isabs(cmdfile) and os.access(os.path.join(path,cmdfile), os.X_OK) ) or \
            ( os.path.isabs(cmdfile) and os.access(cmdfile, os.X_OK) ) ):
            break
      else:
         print "binary '%s' does not exist or is not runnable" % cmdfile
         return

      if readline_callback:
         self.readline_callback = readline_callback

      self.command = command
      self.timeout = timeout
      self.pid = None
      self.state = "STOPPED"

      return

   def wait(self):

      while self.is_active():
         time.sleep(1)

      return

   def is_active(self):

      return self.pid

   def is_running(self):

      return self.state == "RUNNING"

   def is_frozen(self):

      return self.state == "FROZEN"

   def freeze(self):

      if not self.is_frozen():
         return False

      os.kill(self.pid, signal.SIGCONT)
      self.state = "RUNNING"
      return True

   def resume(self):

      if not self.is_running():
         return False

      os.kill(self.pid, signal.SIGSTOP)
      self.state = "FROZEN"
      return True

   def run(self):

      # these are file descriptors, not file objects
      r, w = os.pipe()

      pid = os.fork()

      if pid == os.getpid():
         print "WTF!!!", pid, os.getpid()
         pdb.set_trace()

      if pid:
         # we are the parent
         self.pid = pid
         self.state = "RUNNING"

         os.close(w) # use os.close() to close a file descriptor
         r_fd = os.fdopen(r) # turn r into a file object
         stime = time.time()
         txt = ""
         sts = -1
         print 'forked command "%s" with pid %d, timeout is %d' % (self.command, pid, self.timeout)
         while True:

            # read output from pipe
            ready = select.select([r], [], [], 1)

            if r in ready[0]:
               buf_txt = r_fd.readline()

               if len( buf_txt ) > 1 and hasattr(self, "readline_callback"):
                  self.readline_callback( buf_txt.strip() )
               else:
                  txt = txt + buf_txt

            # is child still running ?
            try:    os.waitpid(pid, os.WNOHANG)
            except:
               # not running, make sure the child process gets cleaned up
               try:    sts = os.waitpid(pid, 0)[1]
               except: pass
               break

            # has timeout passed ?
            if time.time() - stime > self.timeout:
               print 'killing hung child with pid %s after %d seconds (command was "%s")' % (pid,self.timeout,self.command)
               try:    os.kill(pid, signal.SIGKILL)
               except: pass
               break

         self.state = "FINISHED"
         self.pid = None

         if txt[-1:] == '\n': txt = txt[:-1]
         return (sts, txt, time.time()-stime)

      else:

         # we are the child
         os.dup2(r, 0)
         os.dup2(w, 1)
         os.dup2(w, 2)

         import resource
         maxfd = resource.getrlimit(resource.RLIMIT_NOFILE)[1] 
         if not hasattr(resource, "RLIM_INFINITY"):
            resource.RLIM_INFINITY = -1L
         if (maxfd == resource.RLIM_INFINITY): 
            maxfd = MAXFD 
         for fd in range(3, maxfd): 
            try:            os.close(fd) 
            except OSError: pass
         os.execl("/bin/sh", "/bin/sh", "-c", self.command)
         os._exit(127)
